package weixin

import (
	"context"
	"crypto/md5"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"gitee.com/filters/utils/conversion"
	"gitee.com/filters/utils/httpv"
	"github.com/rs/zerolog/log"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"time"
)

type Boter struct {
	key     string
	Content []byte
	MenList []string
	Mobile  []string
}
type OptBot func(bot *Boter)

func WithKey(key string) OptBot {
	return func(wxer *Boter) {
		wxer.key = key
	}
}

type SendTexter struct {
	Msgtype string `json:"msgtype"`
	Text    Texter `json:"text"`
}

type SendMarker struct {
	Msgtype  string `json:"msgtype"`
	Markdown Texter `json:"markdown"`
}

type SendImageer struct {
	Msgtype string `json:"msgtype"`
	Image   Imager `json:"image"`
}

type SendFileer struct {
	Msgtype string `json:"msgtype"`
	File    Filer  `json:"file"`
}

type Filer struct {
	MediaId string `json:"media_id"`
}

type Imager struct {
	Base64 string `json:"base64"`
	Md5    string `json:"md5"`
}

type SendPicTexter struct {
	Msgtype string `json:"msgtype"`
	News    Lister `json:"news"`
}

type Lister struct {
	Article []Articler `json:"articles"`
}

type Articler struct {
	Title       string `json:"title"`
	Description string `json:"description"`
	Url         string `json:"url"`
	Picurl      string `json:"picurl"`
}

type Texter struct {
	Content             string   `json:"content"`
	MentionedList       []string `json:"mentioned_list"`
	MentionedMobileList []string `json:"mentioned_mobile_list"`
}

type UploadRsp struct {
	Errcode   int32  `json:"errcode"`
	Errmsg    string `json:"errmsg"`
	Type      string `json:"type"`
	MediaId   string `json:"media_id"`
	CreatedAt string `json:"created_at"`
}

func NewBot(opt ...OptBot) *Boter {
	bot := &Boter{}
	for _, option := range opt {
		option(bot)
	}
	return bot
}

// AccessToken ...
func (w *Boter) AccessToken() (string, error) {
	return "", nil
}

// SetContent ...
func (w *Boter) SetContent(data interface{},name ...string) *Boter {
	t := reflect.TypeOf(data)
	v := reflect.ValueOf(data)
	if t.Kind() == reflect.String {
		w.Content = []byte(data.(string))
		return w
	}
	if t.Kind() == reflect.Slice {
		if _, ok := data.([]byte); ok {

			w.Content = data.([]byte)
			return w
		}
		return w
	}
	if t.Kind() == reflect.Map {
		for _, key := range v.MapKeys() {
			if key.Kind() == reflect.String || key.Type().String() == SendFileName {
				if v.MapIndex(key).Kind() == reflect.String {
					rsp1,err := w.uploadPath(key.Interface().(string), v.MapIndex(key).Interface().(string))
					if err != nil {
						panic(err)
					}
					w.Content = []byte(rsp1.MediaId)
					return w
				}
				if v.MapIndex(key).Kind() == reflect.Interface {
					var fileName string
					if _,ok :=v.MapIndex(key).Interface().(*os.File);!ok && len(name) <= 0 {
						panic("文件内容未指定")
					}
					if file,ok :=v.MapIndex(key).Interface().(*os.File);ok{
						fileName = filepath.Base(file.Name())
					}
					if len(name) > 0 {
						fileName = name[0]
					}
					rsp2,err := w.uploadReader(key.Interface().(string), fileName, v.MapIndex(key).Interface().(io.Reader))
					if err != nil {
						panic(err)
					}
					w.Content = []byte(rsp2.MediaId)
					return w
				}
			}
		}
	}
	w.Content = conversion.ToByte(data)
	return w
}

// SendFile 发送图片
func (w *Boter) SendFile() error {
	msg := SendFileer{
		Msgtype: SendFileType,
		File: Filer{
			MediaId: string(w.Content),
		},
	}
	params := map[string]interface{}{}
	err := json.Unmarshal(conversion.ToByte(msg), &params)
	if err != nil {
		return err
	}
	return w.send(params)
}

// SendImage 发送图片消息
func (w *Boter) SendImage() error {
	msg := SendImageer{
		Msgtype: SendImageType,
		Image: Imager{
			Base64: base64.StdEncoding.EncodeToString(w.Content),
			Md5:    fmt.Sprintf("%x", md5.Sum(w.Content)),
		},
	}
	params := map[string]interface{}{}
	err := json.Unmarshal(conversion.ToByte(msg), &params)
	if err != nil {
		return err
	}
	return w.send(params)
}

// SendText ...
func (w *Boter) SendText() error {
	msg := SendTexter{
		Msgtype: SendTextType,
		Text: Texter{
			Content:             string(w.Content),
			MentionedList:       w.MenList,
			MentionedMobileList: w.Mobile,
		},
	}
	params := map[string]interface{}{}
	err := json.Unmarshal(conversion.ToByte(msg), &params)
	if err != nil {
		return err
	}
	return w.send(params)
}

// SendMarkDown ...
func (w *Boter) SendMarkDown() error {
	msg := SendMarker{
		Msgtype: SendMarkDownType,
		Markdown: Texter{
			Content:             string(w.Content),
			MentionedList:       w.MenList,
			MentionedMobileList: w.Mobile,
		},
	}
	params := map[string]interface{}{}
	err := json.Unmarshal(conversion.ToByte(msg), &params)
	if err != nil {
		return err
	}
	return w.send(params)
}

// SendPicText 发送图文消息
func (w *Boter) SendPicText() error {
	var list []Articler
	err := json.Unmarshal(w.Content, &list)
	if err != nil {
		return err
	}
	msg := SendPicTexter{
		Msgtype: SendPicTextType,
		News: Lister{
			Article: list,
		},
	}
	params := map[string]interface{}{}
	err = json.Unmarshal(conversion.ToByte(msg), &params)
	if err != nil {
		return err
	}
	return w.send(params)
}

func (w *Boter) send(params map[string]interface{}) error {
	ctx := context.Background()
	ctx, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Second))
	defer cancel()
	url := fmt.Sprintf(BotSendUrl, QWxBaseUrl, w.key)
	_, err := httpv.HttpPost(ctx, url, map[string]string{}, params)
	return err
}

func (w *Boter) uploadPath(name, path string) (UploadRsp,error) {
	var info UploadRsp
	url := fmt.Sprintf(BotUploadFileUrl, QWxBaseUrl, w.key, "file")
	rsp, err := httpv.New().SetFile(name, path).
		POST(url)
	log.Info().Str("sUrl",url).
		Msg("uploadPath msg")
	if err != nil {
		return info, err
	}
	body := rsp.Body()
	log.Info().Bytes("sBody",body).
		Msg("uploadPath body")
	err = json.Unmarshal(body,&info)
	return info, err
}

func (w *Boter) uploadReader(name, fileName string, data io.Reader) (UploadRsp,error) {
	var info UploadRsp
	url := fmt.Sprintf(BotUploadFileUrl, QWxBaseUrl, w.key, "file")
	rsp, err := httpv.New().SetFileReader(name, fileName, data).
		POST(url)
	log.Info().Str("sUrl",url).
		Msg("uploadReader msg")
	if err != nil {
		return info, err
	}
	body := rsp.Body()
	log.Info().Bytes("sBody",body).
		Msg("uploadReader body")
	err = json.Unmarshal(body,&info)
	return info, err
}
